home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #2 / Monster Media No. 2 (Monster Media)(1994).ISO / prog_pas / sk210f.zip / SHUTILPK.PAS < prev    next >
Pascal/Delphi Source File  |  1994-05-11  |  23KB  |  719 lines

  1. {$I SHDEFINE.INC}
  2.  
  3. {$I SHUNITSW.INC}
  4. {$O-}
  5.  
  6. {$D-,L-}
  7.  
  8. unit ShUtilPk;
  9. {
  10.                                 ShUtilPk
  11.  
  12.                              A Utility Unit
  13.  
  14.                                    by
  15.  
  16.                               Bill Madison
  17.  
  18.                    W. G. Madison and Associates, Ltd.
  19.                           13819 Shavano Downs
  20.                             P.O. Box 780956
  21.                        San Antonio, TX 78278-0956
  22.                              (512)492-2777
  23.                              CIS 73240,342
  24.                 Internet bill.madison@lchance.sat.tx.us
  25.  
  26.                 Copyright 1990, '94 Madison & Associates
  27.                           All Rights Reserved
  28.  
  29.         This file may  be used and distributed  only in accord-
  30.         ance with the provisions described on the title page of
  31.                   the accompanying documentation file
  32.                               SKYHAWK.DOC
  33. }
  34.  
  35. Interface
  36.  
  37. Uses
  38.   TpCrt,
  39.   TpString,
  40.   TpDos,
  41.   Dos;
  42.  
  43. const
  44.   Copyr = 'Copyright 1990, 1994 by W.G. Madison';
  45.  
  46. type
  47.   CharSet       = set of char;
  48.   DelimSetType  = set of char;
  49.  
  50. const
  51.   DelimSet  : DelimSetType  = [#0..#32];
  52.  
  53. {*****************************************************************}
  54. { !!!!!!!!!!!!!!!!! NEVER MODIFY THESE VARIABLES !!!!!!!!!!!!!!!!!}
  55. {*****************************************************************}
  56. Var
  57.  
  58.   StartingMode : Byte;
  59. {Initial video mode of the system (Mono, CO80, BW40, ...)}
  60.  
  61.   StartingAttr : Byte;
  62. {Initial video attribute of the system}
  63.  
  64. {*****************************************************************}
  65. {*****************************************************************}
  66.  
  67. function BetwS(Lower, Item, Upper  : LongInt) : boolean;
  68. {Performs a SIGNED test of the condition that Lower <= Item <= Upper,
  69.  returning TRUE if and only if the condition is met. Lower, Item, and
  70.  Upper can be any combination of 1, 2, and 4-byte entities.}
  71.  
  72. {**********************************************************************}
  73.  
  74. function BetwU(Lower, Item, Upper  : LongInt) : boolean;
  75. {Performs an UNSIGNED test of the condition that Lower <= Item <= Upper,
  76.  returning TRUE if and only if the condition is met. Lower, Item, and
  77.  Upper can be any combination of 1, 2, and 4-byte entities.}
  78.  
  79. {**********************************************************************}
  80.  
  81. Function StarString(Pattern, Target : String) : Boolean;
  82. {This function performs a generalization of the wildcard string
  83.  matching usually performed by DOS. A '*' wild card can be placed
  84.  anywhere within the pattern string, and will represent its usual
  85.  'zero or more of any characters'. Scanning will not be terminated
  86.  at that point, however, but will continue. Thus, '*B*EFG' will match
  87.  'ABCDEFG', but '*B*EGF' will not. Similarly, '*ABC*' will match, but
  88.  '*ABC' will not.}
  89.  
  90. {**********************************************************************}
  91.  
  92. Function WhoAmI : String;
  93. {Returns the fully qualified path to the currently executing file.
  94.  *** DOS 3.x or above, ONLY ***}
  95.  
  96. {**********************************************************************}
  97.  
  98. function SearchEnvironment(Code : String) : String;
  99. {Searches the environment space for "CODE" and returns the corresponding
  100.  string.}
  101.  
  102. {**********************************************************************}
  103.  
  104. Function LoWord(LI : LongInt) : Word;
  105. {Returns the low order word of a LongInt.}
  106.  
  107. {**********************************************************************}
  108.  
  109. Function HiWord(LI : LongInt) : Word;
  110. {Returns the high order word of a LongInt.}
  111.  
  112. {**********************************************************************}
  113.  
  114. Function LI(Ilo, Ihi : Word) : LongInt;
  115. {Converts two Word vbls to a LongInt}
  116.  
  117. {**********************************************************************}
  118.  
  119. Function HEX(A : LongInt) : String;
  120. {Converts a byte vbl into a string correspnoding to the hex value.}
  121. {NOTE: The parameter A may be of any Integer type (ShortInt, Byte,
  122.  Integer, Word, or LongInt}
  123. {HEX will return either a 2, 4, or 8 character string, depending on
  124.  whether the actual value of the parameter is representable as a
  125.                           1 byte value (ShortInt, Byte)
  126.                           2 byte value (Integer, Word)
  127.                           4 byte value (LongInt)
  128.  Note that a negative value will always be returned as an 8 character
  129.  string.}
  130.  
  131. {**********************************************************************}
  132.  
  133. Function Pmod(x, modulus : LongInt) : LongInt;
  134. {Returns the mod as a positive number, regardless of the sign of X.
  135.  Recall that, e.g., -1 is congruent to (modulus-1). Thus, for example,
  136.  Pmod(-2, 7) will return 5 as the function value.}
  137.  
  138. {**********************************************************************}
  139.  
  140.   Procedure RepAll(S1, FS, SS : string; var S2 : string);
  141.   {In string S1 replace all occurrences of FS with SS, giving S2}
  142.  
  143.   function RepAllF(S1, FS, SS : string) : string;
  144.  
  145. {**********************************************************************}
  146.  
  147.   Procedure DelAll(S1, DS : string; var S2 : string);
  148.   {In string S1 delete all occurrences of DS, giving S2}
  149.  
  150.   function DelAllF(S1, DS : string) : string;
  151.  
  152. {**********************************************************************}
  153.  
  154. function PosSet(A : CharSet; S : string) : byte;
  155. {Returns the position of the first occurrance of any member of A in S}
  156.  
  157. {**********************************************************************}
  158.  
  159.   Procedure GetNext(var S1, S2 : String);
  160.   {Extracts the next substring from S1 delimited by a member of DelimSet
  161.   and returns it in S2. S1 is returned with the sub-string stripped off.
  162.   If S1 is empty on entry, both S1 and S2 will be empty on return.}
  163.  
  164.   function GetNextF(var S1 : string) : string;
  165.  
  166. {**********************************************************************}
  167.  
  168.  
  169. function UniqueFileName(Path : string; AddExt : boolean) : string;
  170. {Returns a file name which will be unique in the directory specified
  171.  by PATH. On return, the file name will be appended to PATH. If AddExt
  172.  is TRUE, an extension of .$$$ will be appended, else only the file name
  173.  will be returned.}
  174.  
  175. {**********************************************************************}
  176.  
  177.  
  178. Implementation
  179. {------------}
  180.  
  181. var
  182.   Regs : Registers;
  183.   XY   : WindowCoordinates;
  184.  
  185. {**********************************************************}
  186.  
  187. function BetwS(Lower, Item, Upper  : LongInt) : boolean;
  188. {Performs a SIGNED test of the condition that Lower <= Item <= Upper,
  189.  returning TRUE if and only if the condition is met. Lower, Item, and
  190.  Upper can be any combination of 1, 2, and 4-byte entities.}
  191.   begin
  192.     BetwS := (Item >= Lower) and (Item <= Upper);
  193.     end;
  194.  
  195. {**********************************************************}
  196.  
  197. function BetwU(Lower, Item, Upper  : LongInt) : boolean;
  198. {Performs an UNSIGNED test of the condition that Lower <= Item <= Upper,
  199.  returning TRUE if and only if the condition is met. Lower, Item, and
  200.  Upper can be any combination of 1, 2, and 4-byte entities.}
  201.   const
  202.   {In the following table, columns represent hi-word states,
  203.    rows represent lo-word states.
  204.  
  205.       1. a < b, b < c     4. a = b, b < c     7. a > b, b < c
  206.       2.        b = c     5.        b = c     8.        b = c
  207.       3.        b > c     6.        b > c     9.        b > c }
  208.  
  209.     ST  : array[1..9,1..9] of boolean =
  210.       ((  true,  true, false,  true,  true, false, false, false, false),
  211.        (  true,  true, false,  true,  true, false, false, false, false),
  212.        (  true, false, false,  true, false, false, false, false, false),
  213.        (  true,  true, false,  true,  true, false, false, false, false),
  214.        (  true,  true, false,  true,  true, false, false, false, false),
  215.        (  true, false, false,  true, false, false, false, false, false),
  216.        (  true,  true, false, false, false, false, false, false, false),
  217.        (  true,  true, false, false, false, false, false, false, false),
  218.        (  true, false, false, false, false, false, false, false, false));
  219.  
  220.   type
  221.     WO  = ( HW, LW );
  222.     X   = record
  223.             case byte of
  224.               1 : (L : LongInt);
  225.               2 : (W : array[ WO ] of word);
  226.               end;
  227.     LT  = 1..3;
  228.   var
  229.     HiState,
  230.     LoState   : byte;
  231.   function LEG(A, B : word) : LT;
  232.   {Returns 1, 2, 3 as A is <, =, > B}
  233.     begin
  234.       if A < B then
  235.         LEG := 1
  236.       else if A = B then
  237.           LEG := 2
  238.         else
  239.           LEG := 3;
  240.       end;
  241.   begin
  242.     HiState := (3 * LEG(X(Lower).W[HW], X(Item).W[HW]) - 2) +
  243.                (LEG(X(Item).W[HW], X(Upper).W[HW]) - 1);
  244.     LoState := (3 * LEG(X(Lower).W[LW], X(Item).W[LW]) - 2) +
  245.                (LEG(X(Item).W[LW], X(Upper).W[LW]) - 1);
  246.     BetwU := ST[HiState, LoState];
  247.     end;
  248.  
  249. {**********************************************************}
  250.  
  251. Function StarString;
  252. {StarString is a Boolean function which returns True if a pattern
  253.  string possibly containing one or more '*' wild cards matches a
  254.  target. It works by repeatedly extracting maximum length sub-
  255.  strings not containing a * from Pattern, determining if that sub-
  256.  string exists in Target, and, if so, deleting from Target the first
  257.  character through the end of the partial pattern. A final test is
  258.  made on the residual portion of each to determine the final truth
  259.  value of the function. Character wild cards ('?') are handled by
  260.  substituting characters 1-for-1 from the target string into the
  261.  earliest possible match and proceeding as if they were non-existant.
  262.  The function will terminate as soon as the truth value can be
  263.  determined, so that no time is wasted in execution.}
  264.   var
  265.     Index   : Byte;
  266.     TrialB  : String;
  267.  
  268.   procedure ReplQ(var Pattern1 : String; Target1 : String);
  269.   {Replaces all occurrences of '?' in Pattern1 with the corresponding
  270.    character from Target1. If Target1[0] < Pattern1[0], any '?' occurring
  271.    in the tail will not be effected.}
  272.     var
  273.       T1 : Byte;
  274.     begin
  275.       T1 := Pos('?', Pattern1);
  276.       While (T1 <> 0) and (T1 <= Byte(Pattern1[0])) do begin
  277.         Pattern1[T1] := Target1[T1];
  278.         T1 := Pos('?', Pattern1);
  279.         end;
  280.       end; {ReplQ}
  281.  
  282.   procedure Split(Instr : String; Ch : Char; var Before, After : String;
  283.                   var Index : Byte);
  284.   {Splits Instr on the first occurrence of the character Ch. The products
  285.    of the split are returned in Before and After. Ch itself is discarded.
  286.    Index returns the character position in Instr at which the split
  287.    occurred. (0 means no split)}
  288.     begin
  289.      Index := Pos(Ch, Instr);
  290.      Before := Copy(Instr, 1, Index - 1);
  291.      Delete(Instr, 1, Index);
  292.      After := Instr;
  293.      end; {Split}
  294.  
  295.   procedure CountOccur(PatStr, InStr : String; var Count : Byte);
  296.   {Counts the number of occurrences of PatStr in Instr and returns the
  297.    count in Count}
  298.     var
  299.       T1  : Byte;
  300.     begin
  301.       Count := 0;
  302.       T1 := Pos(PatStr, InStr);
  303.       While T1 <> 0 do begin
  304.         Inc(Count);
  305.         Delete(Instr, 1, T1);
  306.         T1 := Pos(PatStr, Instr);
  307.         end;
  308.       end; {CountOccur}
  309.  
  310.   procedure BuildMatch(var Pattern1, Target1 : String; var Index1 : Byte);
  311.   {If possible, constructs the version of Pattern1 which matches the
  312.    earliest substring of Target1 by eliminating character wild cards.
  313.    The position is returned in Index1}
  314.     var
  315.       Pat1  : String;
  316.       T1,           {Pointer within Target1 to start of trial match }
  317.       T2,           {FOR loop index for character replacement       }
  318.       T3,           {Number of character wild cards in Pat1         }
  319.       T4    : Byte; {Position of the T3th character wild card       }
  320.     begin
  321.       If Pattern1 = '' then exit;
  322.       If Pos('?', Pattern1) = 0 then begin
  323.         Index1 := Pos(Pattern1, Target1);
  324.         exit;
  325.         end;
  326.       T1 := 0;
  327.       Pat1 := Pattern1;
  328.       CountOccur('?', Pat1, T3);
  329.       Index1 := Pos(Pat1, Target1);
  330.       While ((T1 + Byte(Pat1[0])) <= Byte(Target1[0])) and
  331.              (Index1 = 0) do begin
  332.         For T2 := 1 to T3 do begin
  333.           T4 := Pos('?',Pat1);
  334.           Pat1[T4] := Target1[T1+T4];
  335.           end; {For}
  336.         Index1 := Pos(Pat1, Target1);
  337.         If Index1 = 0 then
  338.           Pat1 := Pattern1
  339.         else
  340.           Pattern1 := Pat1;
  341.         Inc(T1);
  342.         end; {While}
  343.       end; {BuildMatch}
  344.  
  345.   begin {StarString}
  346.  
  347.     {First, take care of all the special cases}
  348.  
  349.     While Pos('**', Pattern) <> 0 do
  350.       Delete(Pattern, Pos('**', Pattern), 1);
  351.  
  352.     If (Byte(Pattern[0]) = 0) or           {No pattern string  }
  353.        (Byte( Target[0]) = 0) then begin   {or no target string}
  354.       StarString := False;
  355.       Exit;
  356.       end;
  357.  
  358.     If Pattern[1] = '?' then
  359.       Pattern[1] := Target[1];
  360.  
  361.     If Pos('*', Pattern) = 0 then begin    {No wild cards, so }
  362.       ReplQ(Pattern, Target);              {Quick result known}
  363.       StarString := (Pattern = Target);
  364.       Exit;
  365.       end;
  366.  
  367.     Split(Pattern, '*', TrialB, Pattern, Index);
  368.     BuildMatch(TrialB, Target, Index);
  369.     If Index <> 1 then begin               {No match possible }
  370.       StarString := False;
  371.       exit;
  372.       end;
  373.  
  374.     {End of special cases. Proceed with normal processing}
  375.  
  376.     Pattern := TrialB + '*' + Pattern;     {Possible match, so  }
  377.                                            {reconstruct Pattern }
  378.                                            {and proceed         }
  379.  
  380.     While (Pos('*', Pattern) <> 0) do begin  {Still more wild cards}
  381.       Split(Pattern, '*', TrialB, Pattern, Index);
  382.                                              {Disect the pattern   }
  383.  
  384.       {TrialB now contains that portion to the left of the wildcard,
  385.        and Pattern contains what was to the right. The wild card
  386.        itself has been discarded.}
  387.  
  388.       {From TrialB build the best possible match to Target, getting
  389.        rid of character wild cards. Put the expanded string back into
  390.        TrialB for further processing.}
  391.  
  392.       BuildMatch(TrialB, Target, Index);     {Try to find a match  }
  393.                                              { and set the Index   }
  394.  
  395.       If Index = 0 then begin                {No match is possible }
  396.         StarString := False;
  397.         exit;
  398.         end
  399.       else begin                              {Still possible match}
  400.         Delete(Target, 1, Index + Byte(TrialB[0]) - 1);
  401.         end;                                  {Strip off past the  }
  402.       end; {While}                            { last left pattern  }
  403.                                               { and try again      }
  404.       If Byte(Pattern[0]) = 0 then     {'*' as last character of Pattern}
  405.         StarString := True             { so we know there is a match.   }
  406.  
  407.       else begin        { Make sure we are looking at *last* occurrance }
  408.                         {                          of Pattern in Target }
  409.         Index := Pos(Pattern, Target);
  410.         TrialB := Target;                     { Save the current target }
  411.         While Index <> 0 do begin
  412.           Delete(Target, 1, Index + Byte(Pattern[0]) - 1);
  413.                                         { Delete through end of Pattern }
  414.           Index := Pos(Pattern, Target);
  415.           If Index <> 0 then TrialB := Target;    { Save the new target }
  416.           end;
  417.  
  418.         { TrialB now contains the maximum length substring of Target    }
  419.         { which contains the *last* occurrance of Pattern.              }
  420.  
  421.         BuildMatch(Pattern, TrialB, Index);
  422.         If Index = 0 then
  423.           StarString := False
  424.         else
  425.           StarString := ((Index + Byte(Pattern[0]) - 1) = Byte(TrialB[0]));
  426.         end;
  427.     end; {Function StarString}
  428.  
  429. {***************************************************************}
  430.  
  431. function WhoAmI;
  432. var
  433.   s, o  : integer;
  434.   c     : string;
  435. begin
  436.   s := memw[PrefixSeg:$2c];    {the segment address of the start of   }
  437.   o := 0;                      { the environment area at PrefixSeg:$2c}
  438.   while memw[s:o] <> 0 do      {search for end of environment         }
  439.     o := succ(o);              {  which is marked by two 0 bytes      }
  440.   o := o + 4;                  {skip across word count       }
  441.   c := '';
  442.   repeat
  443.     c := c + chr(mem[s:o]);    {transfer fully qualified path       }
  444.     o := succ(o);              {  as a legitimate TurboPASCAL string}
  445.     until mem[s:o] = 0;
  446.   WhoAmI := c;
  447.   end;
  448.  
  449. {**********************************************************************}
  450.  
  451. function searchenvironment;
  452.   var
  453.    x,y   : integer;
  454.    cs    : string;
  455.   begin
  456.    x := memw[prefixseg:$2C];
  457.    y := 0;
  458.    while memw[x:y] <> 0 do begin
  459.     if chr(mem[x:y]) = code[1] then begin
  460.      cs := '';
  461.      repeat                           {copy up to the '='}
  462.       cs := cs + chr(mem[x:y]);
  463.       y := y + 1
  464.       until chr(mem[x:y]) = '=';
  465.      if cs = code then begin          {got a match, so}
  466.       y := y + 1;                       {space across the '='}
  467.       cs := '';
  468.       repeat                            {and copy what's on the other side}
  469.        cs := cs + chr(mem[x:y]);
  470.        y := y + 1
  471.        until mem[x:y] = 0;
  472.       searchenvironment := cs;          {and that's the function value..}
  473.       exit                              {so set it and bail out}
  474.       end {if cs = code}
  475.      end {chr(mem[x:y]) = code[1]}
  476.     else                               {no match, so}
  477.      repeat                            {just find the end of the string}
  478.       y := y + 1
  479.       until mem[x:y] = 0;
  480.     y := y + 1;                      {space across string delimiter}
  481.     end; {while}
  482.     searchenvironment := '';
  483.    end; {of searchenvironment}
  484.  
  485. {**********************************************************}
  486.  
  487. Function LoWord;
  488.   type
  489.     XT = array[1..2] of Word;
  490.   var
  491.     X : XT absolute LI;
  492.   begin
  493.     LoWord := X[1];
  494.     end;
  495.  
  496. {**********************************************************************}
  497.  
  498. Function HiWord;
  499.   type
  500.     XT = array[1..2] of Word;
  501.   var
  502.     X : XT absolute LI;
  503.   begin
  504.     HiWord := X[2];
  505.     end;
  506.  
  507. {**********************************************************************}
  508.  
  509. Function LI;
  510. {Converts two Word vbls to a LongInt}
  511. type
  512.   LItype = record
  513.              case Integer of
  514.                1 : (IT : array[1..2] of Integer);
  515.                2 : (LIT: LongInt);
  516.              end;
  517. var
  518.   X : LItype;
  519. begin
  520.   X.IT[1] := Ilo;
  521.   X.IT[2] := Ihi;
  522.   LI := X.LIT;
  523.   end;
  524.  
  525. {**********************************************************************}
  526.  
  527. Function HEX;
  528.   Type
  529.     HexByte = record
  530.                 case Byte of
  531.                   1 : (LI : LongInt);
  532.                   2 : (BY : array[0..3] of Byte);
  533.                   3 : (Ts : array[0..1] of Word);
  534.                 end;
  535.   Const
  536.     B : Array[0..15] of Char =
  537.              ('0','1','2','3','4','5','6','7','8','9',
  538.               'A','B','C','D','E','F');
  539.   Var
  540.     S1 : String;
  541.     T1,
  542.     T2 : Byte;
  543.     HB : HexByte absolute A;
  544.   Begin
  545.     Case HB.Ts[1] of
  546.       0 :  begin
  547.              T2 := 1;           {At most 2 byte vbl}
  548.              Case HB.BY[1] of
  549.                0 : T2 := 0;     {It's a Byte}
  550.                end;
  551.              end;
  552.       else T2 := 3;
  553.       end;
  554.     S1 := '';
  555.     For T1 := T2 downto 0 do
  556.       S1 := S1 + B[HB.BY[T1] shr 4] + B[HB.BY[T1] and $0F];
  557.     HEX := S1;
  558.     end;
  559.  
  560. {**********************************************************************}
  561.  
  562. function Pmod;
  563. begin
  564.   Pmod := ((x mod modulus) + modulus) mod modulus;
  565.   end;
  566.  
  567. {**********************************************************}
  568.  
  569.   Procedure RepAll(S1, FS, SS : string; var S2 : string);
  570.   {In string S1 replace all occurrences of FS with SS}
  571.     var
  572.       T1 : Integer;
  573.       S3  : string;
  574.     begin
  575.       S2 := '';
  576.       while Pos(FS, S1) <> 0 do begin
  577.         T1 := Pos(FS, S1);
  578.         S2 := S2 + copy(S1, 1, pred(T1)) + SS;
  579.         delete(S1, 1, pred(T1) + Length(FS));
  580.         end; {while}
  581.       S2 := S2 + S1;
  582.       end; {RepAll}
  583.  
  584.   function RepAllF(S1, FS, SS : string) : string;
  585.     var
  586.       S2  : string;
  587.     begin
  588.       RepAll(S1, FS, SS, S2);
  589.       RepAllF := S2;
  590.       end; {RepAllF}
  591.  
  592. {**********************************************************}
  593.  
  594.   Procedure DelAll(S1, DS : string; var S2 : string);
  595.   {In string S1 delete all occurrences of DS}
  596.     begin
  597.       RepAll(S1, DS, '', S2);
  598.       end;
  599.  
  600.   function DelAllF(S1, DS : string) : string;
  601.     begin
  602.       DelAllF := RepAllF(S1, DS, '');
  603.       end; {DelAllF}
  604.  
  605. {**********************************************************}
  606.  
  607. function PosSet(A : CharSet; S : string) : byte;
  608.   var
  609.     T1  : byte;
  610.   begin
  611.     T1 := 1;
  612.     while (not (S[T1] in A)) and (T1 < Length(S)) do
  613.       inc(T1);
  614.     if S[T1] in A then
  615.       PosSet := T1
  616.     else
  617.       PosSet := 0;
  618.     end; {PosSet}
  619.  
  620.   function TrimLeadSet(S : string; CS : CharSet) : string;
  621.     var
  622.       L : byte;
  623.     begin
  624.       L := 1;
  625.       while (S[L] in CS) and (L <= byte(S[0])) do
  626.         inc(L);
  627.       if L = 0 then
  628.         TrimLeadSet := ''
  629.       else
  630.         TrimLeadSet := Copy(S, L, 255);
  631.       end; {TrimLeadSet}
  632.  
  633.   function TrimTrailSet(S : string; CS : CharSet) : string;
  634.     begin
  635.       while (S[byte(S[0])] in CS) and (byte(S[0]) > 0) do
  636.         dec(S[0]);
  637.       TrimTrailSet := S;
  638.       end; {TrimTrailSet}
  639.  
  640.   function TrimSet(S : string; CS : CharSet) : string;
  641.     begin
  642.       TrimSet := TrimTrailSet(TrimLeadSet(S, CS), CS);
  643.       end; {TrimSet}
  644.  
  645.   Procedure GetNext(var S1, S2 : String);
  646.   {Extracts the next space-delimited string from S1 and returns it
  647.   in S2. S1 is returned with the sub-string stripped off.
  648.   If S1 is empty on entry, both S1 and S2 will be empty on return.}
  649.  
  650.   var
  651.     T1 : Integer;
  652.   begin {GetNext}
  653.     If Length(S1) = 0 then begin
  654.       S2[0] := chr(0);
  655.       Exit
  656.       end;
  657.     S1 := TrimSet(S1, DelimSet);     {Strip leading and trailing blanks}
  658.     If Length(S1) = 0 then
  659.       S2[0] := chr(0)
  660.     else
  661.       If PosSet(DelimSet, S1) <> 0 then begin
  662.         T1 := PosSet(DelimSet, S1);
  663.         S2 := Copy(S1, 1, Pred(T1));
  664.         S1 := Copy(S1, T1, Length(S1) - Pred(T1));
  665.         end
  666.       else begin
  667.         S2 := S1;
  668.         S1 := '';
  669.         end;
  670.     end; {GetNext}
  671.  
  672.   function GetNextF(var S1 : string) : string;
  673.   var
  674.     S2 : string;
  675.   begin
  676.     GetNext(S1, S2);
  677.     GetNextF := S2;
  678.     end; {GetNextF}
  679.  
  680. {**********************************************************}
  681.  
  682.  
  683. function UniqueFileName(Path : string; AddExt : boolean) : string;
  684.   var
  685.     FN :  record
  686.             case integer of
  687.               1 : (LI : LongInt);
  688.               2 : (WD : array[1..2] of word);
  689.               end;
  690.     R  :  Registers;
  691.     S  :  string;
  692.  
  693.   begin
  694.     R.AH := $2C;
  695.     MsDos(R);
  696.     FN.WD[1] := R.CX;
  697.     FN.WD[2] := R.DX;
  698.     repeat
  699.       Inc(FN.LI);
  700.       S := Path + HexL(FN.LI);
  701.       if AddExt then S := S + '.$$$';
  702.       until not ExistFile(S);
  703.     UniqueFileName := S
  704.     end;
  705.  
  706.  
  707.  
  708.  
  709. {**********************************************************}
  710.  
  711. begin {Initialization section}
  712.   StartingMode := LastMode;
  713.   With Regs do begin
  714.     AH := 8;
  715.     Intr( $10, Regs );
  716.     StartingAttr := AH;
  717.     end;
  718.   end.
  719.